home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------
- #
- # NewsWatcher - Macintosh NNTP Client Application
- #
- # Written by Steven Falkenburg
- # ©1990 Apple Computer, Inc.
- #
- #-----------------------------------------------------------
- #
- # ftplow.c
- #
- # This file contains the low-level implementation of the
- # file transfer protocol. This protocol is implemented
- # using the TCP protocol, from calls in TCPHi.c and
- # TCPRoutines.c
- #
- #-----------------------------------------------------------*/
-
- #include "compat.h"
- #include <string.h>
- #include <stdio.h>
-
- #ifdef PROTOS
- #include <Types.h>
- #include <Memory.h>
- #include <OSUtils.h>
- #include <Packages.h>
- #include <Desk.h>
- #include <CursorCtl.h>
- #include <Strings.h>
- #include <Errors.h>
- #include <StdIO.h>
- #include <Lists.h>
- #endif
-
- #include "MacTCPCommonTypes.h"
- #include "TCPPB.h"
- #include "TCPHi.h"
- #include "TCPRoutines.h"
- #include "nntp.h"
- #include "FTPLow.h"
- #include "netstuff.h"
- #include "miscstuff.h"
-
- /* local protos */
-
- OSErr SayHello(unsigned long connID);
- OSErr UserAuth(unsigned long connID,char *userID,char *password);
- OSErr SendCommand(unsigned long connID,char *command,Ptr *response,unsigned short *respLen);
- OSErr MakeDataStream(unsigned long *dataConnID,tcp_port lPort,TCPiopb **pBlock);
- OSErr GetDirectData(unsigned long dataConnID,Ptr *directory,TCPiopb *pBlock);
- OSErr SendFileData(unsigned long dataConnID,char *fileName,char *data,TransType tType,long size,TCPiopb *pBlock);
- OSErr RemoveDataStream(unsigned long dataConnID);
- OSErr SendPortComm(unsigned long dataConnID, short portNum);
- tcp_port GetPortNum(void);
- OSErr FTPTextMode(unsigned long connID);
- void FixCRLF(char *data, long *length);
- void AddCRLF(char **data, long *length);
-
- #define cSleepTime 20L
- #define cWaitForOpen 120L
- #define cRBufLen 32768L
- #define cDataLen 32768
- #define cRBufDataLen 65535L
- #define cXferBlockSize 32768
- #define cSendBlockSize 16384
- #define cControlPort 21
- #define cInvalidHost -1
- #define cConnErr -2
- #define cAccessErr -3
- #define cNotCompErr -4
- #define kConnectionTimeOut 25
-
- ProcPtr gStatusProc; /* status procedure for callbacks */
-
-
- /* SayHello is called after a control-port connection has been established.
- It checks the status of the connection.
- */
-
- OSErr SayHello(unsigned long connID)
- {
- OSErr err;
- Ptr dataPtr;
- unsigned short dataLen = cDataLen;
-
- dataPtr = MyNewPtr((unsigned long)cDataLen);
- if (MyMemErr() != noErr)
- return MyMemErr();
- if ((err = RecvData(connID,dataPtr,&dataLen,false)) == noErr) {
- err = (strncmp((char *)dataPtr,"220",3)==0) ? noErr : cConnErr;
- }
- MyDisposPtr(dataPtr);
- return err;
- }
-
-
- /* UserAuth is called once a control connection has been opened and
- checked. It sends the user's name and password across the network
- for authentication. If the user is sucessfully authenticated, a
- code of noErr is returned.
- */
-
- OSErr UserAuth(unsigned long connID,char *userID,char *password)
- {
- OSErr err;
- Ptr sendBuff;
- Ptr respBuff;
- unsigned short respLen;
-
- sendBuff = MyNewPtr((unsigned long) (10+(strlen(userID)>strlen(password) ? strlen(userID) : strlen(password))));
- if (MyMemErr() != noErr)
- return MyMemErr();
- strcpy((char *)sendBuff,"USER ");
- strcat((char *)sendBuff,userID);
- strcat((char *)sendBuff,CRLF);
- if ((err = SendCommand(connID,sendBuff,&respBuff,&respLen)) == noErr) {
- if ((err = (strncmp((char *)respBuff,"331",3)==0) ? noErr : cAccessErr) == noErr) {
- MyDisposPtr(respBuff);
- strcpy((char *)sendBuff,"PASS ");
- strcat((char *)sendBuff,password);
- strcat((char *)sendBuff,CRLF);
- if ((err = SendCommand(connID,sendBuff,&respBuff,&respLen)) == noErr) {
- err = (strncmp((char *)respBuff,"230",3)==0) ? noErr : cAccessErr;
- }
- }
- }
- MyDisposPtr(sendBuff);
- MyDisposPtr(respBuff);
- return err;
- }
-
-
- /* SendCommand is used to send a command along a TCP connection and get a
- response back. It is useful in handshaking protocols such as the one
- used to set up transfers for FTP.
- */
-
- OSErr SendCommand(unsigned long connID,char *command,Ptr *response,unsigned short *respLen)
- {
- OSErr err;
- unsigned short respLeft,oneLen;
- char *bufPtr;
-
- respLeft = (unsigned short) cDataLen - 1;
- *respLen = 0;
- bufPtr = *response = MyNewPtr((unsigned long)cDataLen);
- *bufPtr = '\0';
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- if ((err = SendData(connID,(Ptr)command,(unsigned short)strlen(command),false)) == noErr)
- do {
- oneLen = respLeft;
- err = RecvData(connID,bufPtr,&oneLen,false);
- if (err==noErr) {
- bufPtr += oneLen;
- *bufPtr = '\0';
- *respLen += oneLen;
- respLeft -= oneLen;
- }
- }
- while (err==noErr && respLeft > 0 && *(bufPtr-1)!=LF);
-
- if (err == noErr)
- (gStatusProc)(*response);
-
- return err;
- }
-
-
- /* MakeDataStream is called when a data transfer is about to be started.
- It sets up a secondary TCP communications channed between the client
- and server for the transfer of data.
- */
-
- OSErr MakeDataStream(unsigned long *dataConnID,tcp_port localPort,TCPiopb **pBlock)
- {
- OSErr err;
- Ptr recvBPtr;
- unsigned long recvBLen = cRBufDataLen;
- long remoteHost = 0;
- short remotePort = 0;
-
- recvBPtr = MyNewPtr(recvBLen);
- if (MyMemErr() != noErr)
- return MyMemErr();
- err = CreateStream(dataConnID,recvBLen);
- if (err == noErr)
- AsyncWaitForConnection(*dataConnID,kConnectionTimeOut,localPort,remoteHost,
- remotePort,pBlock);
- return err;
- }
-
-
- /* GetDirectData is called to receive textual data, such as the response
- from an 'ls' command. The data received is passed back in an allocated
- pointer.
- */
-
- OSErr GetDirectData(unsigned long dataConnID,Ptr *directory,TCPiopb *pBlock)
- {
- OSErr err;
- unsigned short length = cXferBlockSize;
- long remoteHost;
- short remotePort;
- Ptr transferBlock;
- Size totalLength = 0;
- OSErr err2;
- Handle theHndl;
-
- while (pBlock->ioResult > 0)
- GiveTime(cSleepTime);
-
- err = AsyncGetConnectionData(pBlock,&remoteHost,&remotePort);
-
- if (err == noErr) {
- theHndl = MyNewHandle((unsigned long)cXferBlockSize);
- if (MyMemErr() != noErr)
- return MyMemErr();
- HLock(theHndl);
- *directory = transferBlock = *theHndl;
- **directory = '\0';
- do {
- length = cXferBlockSize;
- err = RecvData(dataConnID,transferBlock,&length,false);
- if (err == noErr || err == connectionClosing) {
- totalLength += length;
- HUnlock(theHndl);
- MySetHandleSize(theHndl,totalLength+cXferBlockSize);
- HLock(theHndl);
- *directory = *theHndl;
- if ((err2 = MyMemErr()) != noErr) {
- MyDisposHandle(theHndl);
- return err2;
- }
- transferBlock += (unsigned long) length;
- }
- GiveTime(cSleepTime);
- } while (err == noErr);
- if (err == connectionClosing) {
- err = noErr;
- if (totalLength)
- FixCRLF(*directory,&totalLength);
- *directory = MyNewPtr(totalLength);
- if ((err = MyMemErr()) == noErr)
- BlockMove(*theHndl,*directory,totalLength);
- }
- HUnlock(theHndl);
- MyDisposHandle(theHndl);
- }
- return err;
- }
-
-
- /* SendFileData is called once a transfer has been initiated to send data
- from a client to a server. The data is sent out along the secondary
- data channel which has been negotiated between the hosts.
- */
-
- /* warning-- char *data is disposed by this routine!!!!!!!!! */
-
- OSErr SendFileData(unsigned long dataConnID,char *fileName,char *data,
- TransType tType,long size,TCPiopb *pBlock)
- {
- #pragma unused (fileName,tType)
-
- OSErr err;
- long remoteHost;
- short remotePort;
- long index;
- unsigned short sendLen;
-
- /* wait for connect to complete */
-
- while (pBlock->ioResult > 0)
- GiveTime(cSleepTime);
-
- /* get completion code from data connection opening */
-
- err = AsyncGetConnectionData(pBlock,&remoteHost,&remotePort);
-
- if (err == noErr) {
- AddCRLF(&data,&size);
- index = 0;
- while (index < size && err==noErr) {
- sendLen = (unsigned short)cSendBlockSize;
- if ((index+(unsigned long)sendLen) > size)
- sendLen = (unsigned short)size-(unsigned short)index;
- err = SendData(dataConnID,data+index,sendLen,false);
- index += cSendBlockSize;
- }
- MyDisposPtr(data);
- }
-
- return err;
- }
-
-
- /* RemoveDataStream is called once a data transfer has been completed.
- It shuts down the secondary data channel between the client and
- server which was used solely for one data transfer.
- */
-
- OSErr RemoveDataStream(unsigned long dataConnID)
- {
- OSErr err;
-
- CloseConnection(dataConnID);
- err = ReleaseStream(dataConnID);
- return err;
- }
-
-
- /* GetPortNum is a routine which picks a random port number for use in data
- transfers.
- */
-
- tcp_port GetPortNum(void)
- {
- return (40000 + (((unsigned long) TickCount()) & 0x3fff)); /* get a random port */
- }
-
-
- /* SendPortComm is called to send the 'PORT' command, telling the server which
- port is to be used for the transfer of data between server and client.
- */
-
- OSErr SendPortComm(unsigned long dataConnID, short portNum)
- {
- OSErr err;
- Ptr respBuff;
- unsigned short respLen;
- ip_addr myNetNum;
- char commandStr[40];
-
- if ((err = GetMyIP(&myNetNum)) == noErr) {
- sprintf(commandStr,"PORT %lu,%lu,%lu,%lu,%u,%u",
- ( myNetNum >> 24), /* converts addr to dot notation */
- ((myNetNum & 0x00FF0000) >> 16),
- ((myNetNum & 0x0000FF00) >> 8),
- ( myNetNum & 0x000000FF),
- ( portNum >> 8),
- ( portNum & 0x00FF));
- strcat(commandStr,CRLF);
- err = SendCommand(dataConnID,commandStr,&respBuff,&respLen);
- MyDisposPtr(respBuff);
- }
- return err;
- }
-
-
- /* FTPTextMode is called to switch the mode of the data transfer to text mode.
- */
-
- OSErr FTPTextMode(unsigned long connID)
- {
- Ptr respBuff;
- unsigned short respLen;
- OSErr err;
- char commandStr[256];
-
- strcpy(commandStr,"TYPE A");
- strcat(commandStr,CRLF);
-
- if ((err = SendCommand(connID,commandStr,&respBuff,&respLen)) == noErr) {
- err = (strncmp((char *)respBuff,"200",3)==0);
- }
- MyDisposPtr(respBuff);
- return err;
- }
-
-
- /* FixCRLF removes the LF from CRLF pairs received on the data connection.
- */
-
- void FixCRLF(char *data, long *length)
- {
- register char *source,*dest;
-
- if (*length > 0) {
- source = dest = data;
- while ((source - data) < (*length-1)) {
- if (*source == LF)
- source++;
- *dest++ = *source++;
- }
- if (*source != LF && (source - data) < *length)
- *dest++ = *source++;
- *length = dest - data;
- }
- }
-
-
- /* AddCRLF adds a LF to each CR for data to be sent to the remote host
- in text (ascii) mode.
- */
-
- void AddCRLF(char **data, long *length)
- {
- register char *curOld,*curNew,*newData;
-
- newData = curNew = MyNewPtr((unsigned long)cSendBlockSize*2);
- if (MyMemErr() != noErr)
- return;
-
- curOld = *data;
-
- if (*curOld == LF)
- curOld++;
-
- while ( (curOld-*data) < *length ) {
- *curNew++ = *curOld++;
- if (*(curOld-1) == CR && *curOld != LF)
- *curNew++ = LF;
- }
-
- *length = curNew-newData;
- MyDisposPtr(*data);
- *data = newData;
- }
-
-
- /*------------------------------------------------------------*/
-
-
- /* FTPInit is called to initialize the necessary drivers for FTP connections.
- In addition, it registers a callback routine for status messages.
- */
-
- OSErr FTPInit(ProcPtr statusproc)
- {
- gStatusProc = statusproc;
- return InitNetwork();
- }
-
-
- /* FTPFinish is called when all FTP transactions have been completed.
- In this implementation, the routine actually does nothing.
- */
-
- OSErr FTPFinish(void)
- {
- return noErr;
- }
-
-
- /* FTPConnect sets up an FTP control connection between the local machine
- and a remote FTP server.
- */
-
- OSErr FTPConnect(unsigned long *connID,char *address,char *userID,char *password)
- {
- ip_addr connAddr;
- tcp_port lPort = 0;
- OSErr err = cInvalidHost;
-
- if (((err = ConvertStringToAddr(address,&connAddr)) == noErr) &&
- ((err = CreateStream(connID,cRBufLen)) == noErr)) {
- if (((err = OpenConnection(*connID,connAddr,cControlPort,kConnectionTimeOut)) == noErr) &&
- ((err = SayHello(*connID)) == noErr))
- err = UserAuth(*connID,userID,password);
- if (err != noErr)
- FTPDisconnect(*connID);
- }
- return err;
- }
-
-
- /* FTPDisconnect shuts down an FTP connection which was opened using FTPConnect.
- */
-
- OSErr FTPDisconnect(unsigned long connID)
- {
- OSErr err;
- Ptr recvPtr;
- unsigned short respLen;
- char commandStr[256];
-
- strcpy(commandStr,"QUIT");
- strcat(commandStr,CRLF);
- SendCommand(connID,commandStr,&recvPtr,&respLen);
- MyDisposPtr(recvPtr);
-
- CloseConnection(connID);
- err = ReleaseStream(connID);
- return err;
- }
-
-
- /* FTPViewFile is called to get a text-mode file from a remote machine
- and store the contents of the file in a pointer allocated within
- the call.
- */
-
- OSErr FTPViewFile(unsigned long connID,Ptr *file,char *fileName)
- {
- OSErr err = noErr;
- unsigned long dataConnID;
- Ptr respBuff,dataPtr;
- unsigned short respLen;
- short dataPort;
- unsigned short dataLen = cDataLen;
- char *command;
- TCPiopb *pBlock;
- char compStr[256];
-
- strcpy(compStr,CRLF);
- strcat(compStr,"226");
-
- GiveTime(cSleepTime);
-
- command = MyNewPtr(256);
- if (MyMemErr() != noErr)
- return MyMemErr();
- strcpy(command,"RETR ");
- strcat(command,fileName);
- strcat(command,CRLF);
-
- FTPTextMode(connID);
- dataPort = GetPortNum();
- StatusWindow("Opening Data Connection...",-1);
- GiveTime(0);
- MakeDataStream(&dataConnID,dataPort,&pBlock);
- StatusWindow("Sending PORT Command...",-1);
- GiveTime(0);
- if ((err = SendPortComm(connID,dataPort)) == noErr) {
- StatusWindow("Requesting File...",-1);
- GiveTime(0);
- if (((err = SendCommand(connID,command,&respBuff,&respLen)) == noErr) &&
- ((err = (strncmp((char *)respBuff,"150",3)==0) ? noErr : cNotCompErr) == noErr) &&
- (StatusWindow("Receiving File...",-1)) &&
- ((err = GetDirectData(dataConnID,file,pBlock)) == noErr)) {
- if (!(strstr(respBuff,compStr))) {
- dataPtr = MyNewPtr((unsigned long)cDataLen);
- if (MyMemErr() == noErr) {
- err = RecvData(connID,dataPtr,&dataLen,false);
- MyDisposPtr(dataPtr);
- }
- else err = MyMemErr();
- }
- }
- MyDisposPtr(respBuff);
- }
- RemoveDataStream(dataConnID);
- MyDisposPtr(command);
- return err;
- }
-
-
- /* FTPPutFile sends a buffer from the local machine to the remote host.
- The transfer is completed in text mode.
- */
-
- OSErr FTPPutFile(unsigned long connID,char *fileName,char *data,long size)
- {
- OSErr err = noErr;
- unsigned long dataConnID;
- Ptr respBuff;
- unsigned short respLen;
- tcp_port dataPort;
- char *getCommand;
- unsigned short dataLen = cDataLen;
- TCPiopb *pBlock;
-
- GiveTime(cSleepTime);
-
- FTPTextMode(connID);
-
- getCommand = MyNewPtr(256);
- if (MyMemErr() != noErr)
- return MyMemErr();
-
- strcpy(getCommand,"STOR ");
- strcat(getCommand,fileName);
- strcat(getCommand,CRLF);
-
- dataPort = GetPortNum();
- StatusWindow("Opening Data Connection...",-1);
- GiveTime(0);
- MakeDataStream(&dataConnID,dataPort,&pBlock);
- if ((err = SendPortComm(connID,dataPort)) == noErr) {
- StatusWindow("Requesting Transfer...",-1);
- GiveTime(0);
- if (((err = SendCommand(connID,getCommand,&respBuff,&respLen)) == noErr) &&
- ((err = (strncmp((char *)respBuff,"150",3)==0) ? noErr : cNotCompErr) == noErr)) {
- StatusWindow("Sending Data...",-1);
- GiveTime(0);
- err = SendFileData(dataConnID,fileName,data,text,size,pBlock);
- }
- MyDisposPtr(respBuff);
- }
- RemoveDataStream(dataConnID);
- MyDisposPtr(getCommand);
- SetCursor(&QDARROW);
- return err;
- }
-